package tests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import lib.FileIO;

import org.junit.Test;

/**
 * Unit tests for {@link FileIO}
 * @author abhip
 *
 */
public class FileIOTests
{
    private class Context
    {
        public void CreateFile(File file) throws IOException
        {
            File parentDir = file.getParentFile();
            parentDir.mkdirs();
            file.createNewFile();
        }

        public List<String> GetFileContents(File file) throws IOException
        {
            List<String> lines = new ArrayList<String>();

            BufferedReader br = new BufferedReader(new FileReader(file));
            String curLine = null;
            while ((curLine = br.readLine()) != null)
            {
                lines.add(curLine);
            }

            return lines;
        }

        public void WriteToFile(File file, String data) throws IOException
        {
            if (!file.exists())
            {
                this.CreateFile(file);
            }

            BufferedWriter bufWriter = new BufferedWriter(new FileWriter(file, true));
            bufWriter.write(data);
            bufWriter.newLine();
            bufWriter.close();
        }
    }

    @Test
    public void AppendLineToFile_WhenFileDoesNotExist_CreatesFileWithGivenData() throws IOException
    {
        // Arrange:
        Context context = new Context();
        File tempDir = TestHelpers.GetTempDirectory();
        File testDir = new File(tempDir, "testFolder");
        File testFile = new File(testDir, "testFile.txt");
        String testData = "This is some test data!";

        // Act:
        FileIO.AppendLineToFile(testFile, testData);

        // Assert:
        assertTrue("Test file must be created", testFile.exists());
        List<String> linesRead = context.GetFileContents(testFile);
        assertEquals("Test file must have exactly one line", 1, linesRead.size());
        assertEquals("Contents of testFile should match what was written to it", testData, linesRead.get(0));
    }

    @Test
    public void AppendLineToFile_WhenFileExists_AppendsData() throws IOException
    {
        // Arrange:
        Context context = new Context();
        File tempDir = TestHelpers.GetTempDirectory();
        File testDir = new File(tempDir, "testFolder");
        File testFile = new File(testDir, "testFile.txt");
        String oldTestData = "Old test data";
        String newTestData = "New test data";
        context.WriteToFile(testFile, oldTestData);

        // Act:
        FileIO.AppendLineToFile(testFile, newTestData);

        // Assert:
        assertTrue("Test file must be created", testFile.exists());
        List<String> linesRead = context.GetFileContents(testFile);
        assertEquals("Test file must have 2 lines", 2, linesRead.size());
        assertEquals("Old data should be unchanged", oldTestData, linesRead.get(0));
        assertEquals("New data should be appened", newTestData, linesRead.get(1));
    }

    @Test(expected=IOException.class)
    public void CreateFile_WhenFileAlreadyExists_ThrowsIOException() throws IOException
    {
        // Arrange:
        Context context = new Context();
        File tempDir = TestHelpers.GetTempDirectory();
        File testFile = new File(tempDir, "testFile.txt");
        context.CreateFile(testFile);

        // Act/Assert: This should throw an IOException
        FileIO.CreateFile(testFile);
    }

    @Test
    public void CreateFile_WhenFileDoesNotExist_CreatesFile() throws IOException
    {
        // Arrange:
        File tempDir = TestHelpers.GetTempDirectory();
        File testFile = new File(tempDir, "testFile.txt");

        // Act:
        FileIO.CreateFile(testFile);

        // Assert:
        assertTrue("Test file must be created.", testFile.exists());
    }

    @Test
    public void CreateFile_WhenFileParentFolderDoesNotExist_CreatesFile() throws IOException
    {
        // Arrange:
        File tempDir = TestHelpers.GetTempDirectory();
        File testDir = new File(tempDir, "testFolder");
        File testFile = new File(testDir, "testFile.txt");

        // Act:
        FileIO.CreateFile(testFile);

        // Assert:
        assertTrue("Test file must be created", testFile.exists());
    }

    @Test
    public void GetLineFromFile_GetsLine() throws IOException
    {
        // Arrange:
        Context context = new Context();
        File tempDir = TestHelpers.GetTempDirectory();
        File testDir = new File(tempDir, "testFolder");
        File testFile = new File(testDir, "testFile.txt");
        String data1 = "Test data 1";
        String data2 = "Test data 2";
        context.WriteToFile(testFile, data1);
        context.WriteToFile(testFile, data2);

        // Act:
        String lineRead = FileIO.GetLineFromFile(testFile, 1);

        // Assert:
        assertNotNull("Line read should not be null", lineRead);
        assertEquals("Line read should be 1st line in file", data1, lineRead);
    }

    @Test(expected=IOException.class)
    public void GetLineFromFile_WhenInvalidLineNumber_ThrowsIOException() throws IOException
    {
        // Arrange:
        Context context = new Context();
        File tempDir = TestHelpers.GetTempDirectory();
        File testDir = new File(tempDir, "testFolder");
        File testFile = new File(testDir, "testFile.txt");
        String data1 = "Test data 1";
        String data2 = "Test data 2";
        context.WriteToFile(testFile, data1);
        context.WriteToFile(testFile, data2);
        int invalidLineNumber = 3;

        // Act/Assert: IOException must be thrown
        FileIO.GetLineFromFile(testFile, invalidLineNumber);
    }
}
